﻿using PHMEE.DTOS.StatusMonitor;
using PHMEE.Entities.DaqInfo.TdDevices;
using PHMEE.TDengineInfrastructure;
using PHMEE.ToolKits;
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TDengine.Data.Client;
using static PHMEE.Constants.PhmeeEnum;

namespace PHMEE
{
    public class TdEngineCommonAppService : PHMEEAppService
    {
        private readonly ITDConnectionFactory iTDConnectionFactory;
        private readonly ITdDeviceManager tdDeviceManager;

        public TdEngineCommonAppService(ITDConnectionFactory iTDConnectionFactory, ITdDeviceManager tdDeviceManager)
        {
            this.iTDConnectionFactory = iTDConnectionFactory;
            this.tdDeviceManager = tdDeviceManager;
        }

        /// <summary>
        /// 获取某个测点的TDEngine表源数据特征值-最新一条
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task<WaveDataFeature> GetTdEnginePointFeatureDataNewest(Guid VesselId, TdFeatureInput input)
        {
            List<WaveDataFeature> list = [await GetTdEnginePointFeatureDataNormalNewest(VesselId, input), await GetTdEnginePointFeatureDataAlarmNewest(VesselId, input)];

            WaveDataFeature model = new WaveDataFeature();

            var newestModel = list.OrderByDescending(item => item?.TimeStamp).FirstOrDefault();

            if (newestModel != null)
            {
                model = newestModel;
            }

            return model;
        }

        /// <summary>
        /// 获取某个测点的TDEngine表源数据特征值-正常
        /// </summary>
        /// <param name="VesselId"></param>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task<WaveDataFeature> GetTdEnginePointFeatureDataNormalNewest(Guid VesselId, TdFeatureInput input)
        {
            string dbName = PhmeeConst.GetDbName(DbPrefixEnum.Vb, VesselId);
            string sqlNormalSuper = $"select 1 from {dbName}.wavedata_normal where pointid='{input.PointId}';";
            string sqlNormal = $"select *,chno,id,pointid from {dbName}.wdne_{input.PointFormatGuid()} where ts <= '{input.DateTime.PhmeeToUtcFormatDateTimeString()}'  order by ts desc limit 1 ;";
            WaveDataFeature waveDataFeatureNormal = null;

            using (TDengineConnection dbConnection = iTDConnectionFactory.GetConnection())
            {
                bool resultNormalFlag = true;
                using (DbCommand commandNormalExist = dbConnection.CreateCommand())
                {
                    commandNormalExist.CommandText = sqlNormalSuper;
                    var resultNormal = await commandNormalExist.ExecuteScalarAsync();
                    resultNormalFlag = Convert.ToBoolean(resultNormal);
                }
                if (resultNormalFlag)
                {
                    using (DbCommand commandNormal = dbConnection.CreateCommand())
                    {
                        commandNormal.CommandText = sqlNormal;
                        using (DbDataReader readerNormal = await commandNormal.ExecuteReaderAsync())
                        {
                            while (await readerNormal.ReadAsync())
                            {
                                waveDataFeatureNormal = new WaveDataFeature();
                                waveDataFeatureNormal.Type = ChartEnum.Normal;
                                waveDataFeatureNormal.TimeStamp = readerNormal.GetPhmeeTdEngineValue<DateTime>("ts");
                                waveDataFeatureNormal.AccRms = readerNormal.GetPhmeeTdEngineValue<float?>("acc_rms");
                                waveDataFeatureNormal.AccRmsWarning = readerNormal.GetPhmeeTdEngineValue<float?>("acc_rms_warning");
                                waveDataFeatureNormal.AccRmsAlarm = readerNormal.GetPhmeeTdEngineValue<float?>("acc_rms_alarm");
                                waveDataFeatureNormal.AccAvgPeak = readerNormal.GetPhmeeTdEngineValue<float?>("acc_avg_peak");
                                waveDataFeatureNormal.AccAvgPeakWarning = readerNormal.GetPhmeeTdEngineValue<float?>("acc_avg_peak_warning");
                                waveDataFeatureNormal.AccAvgPeakAlarm = readerNormal.GetPhmeeTdEngineValue<float?>("acc_avg_peak_alarm");
                                waveDataFeatureNormal.VelRms = readerNormal.GetPhmeeTdEngineValue<float?>("vel_rms");
                                waveDataFeatureNormal.VelRmsWarning = readerNormal.GetPhmeeTdEngineValue<float?>("vel_rms_warning");
                                waveDataFeatureNormal.VelRmsAlarm = readerNormal.GetPhmeeTdEngineValue<float?>("vel_rms_alarm");
                                waveDataFeatureNormal.AccEnvAvgPeak = readerNormal.GetPhmeeTdEngineValue<float?>("acc_env_avg_peak");
                                waveDataFeatureNormal.ChNo = readerNormal.GetPhmeeTdEngineValue<int>("chno");
                                waveDataFeatureNormal.Id = Guid.Parse(Encoding.UTF8.GetString(readerNormal.GetPhmeeTdEngineValue<byte[]>("id")));
                                waveDataFeatureNormal.PointId = Guid.Parse(Encoding.UTF8.GetString(readerNormal.GetPhmeeTdEngineValue<byte[]>("pointid")));
                            }
                        }
                    }
                }
            }

            return waveDataFeatureNormal;
        }

        /// <summary>
        /// 获取某个测点的TDEngine表源数据特征值-报警
        /// </summary>
        /// <param name="VesselId"></param>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task<WaveDataFeature> GetTdEnginePointFeatureDataAlarmNewest(Guid VesselId, TdFeatureInput input)
        {
            string dbName = PhmeeConst.GetDbName(DbPrefixEnum.Vb, VesselId);
            string sqlAlarmSuper = $"select 1 from {dbName}.wavedata_alarm where pointid='{input.PointId}';";
            string sqlAlarm = $"select *,chno,id,pointid from {dbName}.wdae_{input.PointFormatGuid()} where ts <= '{input.DateTime.PhmeeToUtcFormatDateTimeString()}'  order by ts desc limit 1 ;";

            WaveDataFeature waveDataFeatureAlarm = null;

            using (TDengineConnection dbConnection = iTDConnectionFactory.GetConnection())
            {
                bool resultAlarmFlag = true;
                using (DbCommand commandAlarmExist = dbConnection.CreateCommand())
                {
                    commandAlarmExist.CommandText = sqlAlarmSuper;
                    var resultAlarm = await commandAlarmExist.ExecuteScalarAsync();
                    resultAlarmFlag = Convert.ToBoolean(resultAlarm);
                }
                if (resultAlarmFlag)
                {
                    using (DbCommand commandAlarm = dbConnection.CreateCommand())
                    {
                        commandAlarm.CommandText = sqlAlarm;
                        using (DbDataReader readerAlarm = await commandAlarm.ExecuteReaderAsync())
                        {
                            while (await readerAlarm.ReadAsync())
                            {
                                waveDataFeatureAlarm = new WaveDataFeature();
                                waveDataFeatureAlarm.Type = ChartEnum.Alarm;
                                waveDataFeatureAlarm.TimeStamp = readerAlarm.GetPhmeeTdEngineValue<DateTime>("ts");
                                waveDataFeatureAlarm.AccRms = readerAlarm.GetPhmeeTdEngineValue<float?>("acc_rms");
                                waveDataFeatureAlarm.AccRmsWarning = readerAlarm.GetPhmeeTdEngineValue<float?>("acc_rms_warning");
                                waveDataFeatureAlarm.AccRmsAlarm = readerAlarm.GetPhmeeTdEngineValue<float?>("acc_rms_alarm");
                                waveDataFeatureAlarm.AccAvgPeak = readerAlarm.GetPhmeeTdEngineValue<float?>("acc_avg_peak");
                                waveDataFeatureAlarm.AccAvgPeakWarning = readerAlarm.GetPhmeeTdEngineValue<float?>("acc_avg_peak_warning");
                                waveDataFeatureAlarm.AccAvgPeakAlarm = readerAlarm.GetPhmeeTdEngineValue<float?>("acc_avg_peak_alarm");
                                waveDataFeatureAlarm.VelRms = readerAlarm.GetPhmeeTdEngineValue<float?>("vel_rms");
                                waveDataFeatureAlarm.VelRmsWarning = readerAlarm.GetPhmeeTdEngineValue<float?>("vel_rms_warning");
                                waveDataFeatureAlarm.VelRmsAlarm = readerAlarm.GetPhmeeTdEngineValue<float?>("vel_rms_alarm");
                                waveDataFeatureAlarm.AccEnvAvgPeak = readerAlarm.GetPhmeeTdEngineValue<float?>("acc_env_avg_peak");
                                waveDataFeatureAlarm.AlarmLevel = readerAlarm.GetPhmeeTdEngineValue<int?>("alarm_level");
                                waveDataFeatureAlarm.ChNo = readerAlarm.GetPhmeeTdEngineValue<int>("chno");
                                waveDataFeatureAlarm.Id = Guid.Parse(Encoding.UTF8.GetString(readerAlarm.GetPhmeeTdEngineValue<byte[]>("id")));
                                waveDataFeatureAlarm.PointId = Guid.Parse(Encoding.UTF8.GetString(readerAlarm.GetPhmeeTdEngineValue<byte[]>("pointid")));
                            }
                        }
                    }

                }

            }

            return waveDataFeatureAlarm;
        }

        /// <summary>
        /// 配置-工艺测点-获取TdEngine统一方法
        /// </summary>
        /// <typeparam name="TModel"></typeparam>
        /// <param name="relation"></param>
        /// <param name="input"></param>
        /// <param name="mapModelFunc"></param>
        /// <returns></returns>
        public async Task FetchTdEngineData<TModel>(Point point, DateTime dateTime, TModel model, Action<DbDataReader, List<TdDeviceAttribute>, TModel> mapModelAction)
        {
            if (point == null)
            {
                return;
            }

            var relation = await tdDeviceManager.GetRelationAsync(point.Id);

            if (relation == null)
            {
                return;
            }

            string databaseName = relation.TdDeviceInstance?.TdDeviceType?.Database?.Name;
            string superName = relation.TdDeviceInstance?.TdDeviceType?.Name;
            string instanceName = relation.TdDeviceInstance?.InstanceName;

            string sqlSuper = $"select 1 from {databaseName}.{superName};";
            string sqlInstance = $"select * from {databaseName}.{instanceName} where ts <= '{dateTime.PhmeeToUtcFormatDateTimeString()}' order by ts desc limit 1;";

            List<TdDeviceAttribute> attributes = await tdDeviceManager.GetAttributes(relation.TdDeviceInstanceId, false);

            using (TDengineConnection connection = iTDConnectionFactory.GetConnection())
            {
                bool resultFlag = true;
                using (DbCommand commandExist = connection.CreateCommand())
                {
                    commandExist.CommandText = sqlSuper;
                    var result = await commandExist.ExecuteScalarAsync();
                    resultFlag = Convert.ToBoolean(result);
                }

                if (resultFlag)
                {
                    using (DbCommand commandReader = connection.CreateCommand())
                    {
                        commandReader.CommandText = sqlInstance;
                        using (DbDataReader reader = await commandReader.ExecuteReaderAsync())
                        {
                            while (await reader.ReadAsync())
                            {
                                mapModelAction(reader, attributes, model);
                            }
                        }
                    }
                }
            }
        }

        /// <summary>
        /// 获取TdEngine映射到具体Model
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="reader"></param>
        /// <param name="attribute"></param>
        /// <returns></returns>
        public T GetTdEngineTypeValue<T>(DbDataReader reader, TdDeviceAttribute attribute)
        {
            if (attribute == null)
            {
                return default;
            }

            object result = default;

            var columnTypeEnum = PHMEECoreHelper.GetEnumByDescription(attribute.ColumnType, typeof(TdEngineAttributeEnum));
            switch (columnTypeEnum)
            {
                case TdEngineAttributeEnum.TIMESTAMP:
                    result = reader.GetPhmeeTdEngineValue<DateTime>(attribute.Name);
                    break;

                case TdEngineAttributeEnum.FLOAT:
                    result = reader.GetPhmeeTdEngineValue<float?>(attribute.Name);
                    break;

                case TdEngineAttributeEnum.INT:
                    result = reader.GetPhmeeTdEngineValue<int?>(attribute.Name);
                    break;

                case TdEngineAttributeEnum.BIGINT_UNSIGNED:
                    result = reader.GetPhmeeTdEngineValue<long?>(attribute.Name);
                    break;

                case TdEngineAttributeEnum.VARCHAR_64000:
                case TdEngineAttributeEnum.NVARCHAR:
                case TdEngineAttributeEnum.VARCHAR_256:
                    result = Encoding.UTF8.GetString(reader.GetPhmeeTdEngineValue<byte[]>(attribute.Name));
                    break;

                case TdEngineAttributeEnum.VARCHAR_36:
                    result = Guid.Parse(Encoding.UTF8.GetString(reader.GetPhmeeTdEngineValue<byte[]>(attribute.Name)));
                    break;
                default:
                    result = Encoding.UTF8.GetString(reader.GetPhmeeTdEngineValue<byte[]>(attribute.Name));
                    break;
            }

            return (T)result;
        }

    }
}
